#include <QSettings>
#include <QFileDialog>
#include <QDebug>
#include <QDateTime>
#include "commconfig.h"
#include "ui_commconfig.h"
#include "MavlinkHandler/mavlinkhandler.h"

///
/// \brief CommConfig::CommConfig
/// \param parent
///
CommConfig::CommConfig(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::CommConfig),
    mConnect(false)
{
    ui->setupUi(this);

    // load the configs ini file
    loadConfigFile();

    // Creat a mavlinkhandler
    mMavlinkHandler = new MavlinkHandler();
}

///
////// \brief mmConfig::~CommConfig
///
CommConfig::~CommConfig()
{
    delete mMavlinkHandler;
    delete ui;
}


///
/// \brief CommConfig::loadConfigFile
/// \return
///
bool CommConfig::loadConfigFile(void)
{
    QString iniName = QCoreApplication::applicationDirPath() + "/Settings.ini";

    //"CommConfig.ini"，open the file if exist,otherwise creat it!
    mConfigSetting = new QSettings(iniName,QSettings::IniFormat);

    // Net
    getConfigNet(mNetIp,mNetPort);

    // Log
    getConfigLog(mLogPath);

    // Select
    getConfigCommSelect(mCommSelect);

    return true;
}

///
/// \brief CommConfig::setConfigNet
/// \param ipaddr
/// \param port
/// \return
///
bool CommConfig::setConfigNet(QString ipaddr, QString port)
{
    if(nullptr == mConfigSetting){
        return false;
    }

    // Update ini file
    mConfigSetting->beginGroup("NET");
    mConfigSetting->setValue("ip",ipaddr);
    mConfigSetting->setValue("port",port);
    mConfigSetting->endGroup();

    // Update local value
    mNetIp = ipaddr;
    mNetPort = port;

    return true;
}

///
/// \brief CommConfig::getConfigNet
/// \param ipaddr
/// \param port
/// \return
///
bool CommConfig::getConfigNet(QString &ipaddr, QString &port)
{

    // Read from ini file
    ipaddr = mConfigSetting->value("/NET/ip").toString();
    port = mConfigSetting->value("/NET/port").toString();

    // Update UI
    ui->leditCommNetIP->setText(ipaddr);
    ui->leditCommNetPort->setText(port);

    return true;
}



///
/// \brief CommConfig::setConfigLog
/// \param logpath
/// \return
///
bool CommConfig::setConfigLog(QString logpath)
{
    if(nullptr == mConfigSetting){
        return false;
    }
    // Update ini file
    mConfigSetting->beginGroup("LOG");
    mConfigSetting->setValue("path",logpath);
    mConfigSetting->endGroup();

    // Update local value
    mLogPath = logpath;

    return true;
}

///
/// \brief CommConfig::getConfigLog
/// \param logpath
/// \return
///
bool CommConfig::getConfigLog(QString &logpath)
{
    // Read from ini file
    logpath = mConfigSetting->value("/LOG/path").toString();

    // Update local value
    ui->leditCommLogPath->setText(logpath);
    return true;
}

///
/// \brief CommConfig::setConfigCommSelect
/// \param select
/// \return
///
bool CommConfig::setConfigCommSelect(int select)
{
    if(nullptr == mConfigSetting){
        return false;
    }
    // Update ini file
    mConfigSetting->beginGroup("SELECT");
    mConfigSetting->setValue("index",select);
    mConfigSetting->endGroup();

    // Update local value
    mCommSelect = select;

    return true;
}

///
/// \brief CommConfig::getConfigCommSelect
/// \param select
/// \return
///
bool CommConfig::getConfigCommSelect(int &select)
{
    // Read from ini file
    select = mConfigSetting->value("/SELECT/index").toInt();

    // Update local value
    ui->cboxCommSelect->setCurrentIndex(select);

    return true;
}


///
/// \brief CommConfig::on_btnCommLogFile_released
///
void CommConfig::on_btnCommLogFile_released()
{
    QString file = QFileDialog::getOpenFileName(
            this,
            tr("Selcct a log file."),
            mLogPath,
            tr("raw data stream (*.dat);;data base files(*.db)"));
    if (!file.isEmpty()) {
        ui->leditCommLogFile->setText(QFileInfo(file).fileName());
    }
}

///
/// \brief CommConfig::on_btnCommLogPath_released
///
void CommConfig::on_btnCommLogPath_released()
{

    QFileDialog *fileDialog = new QFileDialog(this);
    fileDialog->setFileMode(QFileDialog::Directory);
    fileDialog->exec();

    if(!fileDialog->selectedFiles().at(0).isEmpty()){

        // Update ui
        ui->leditCommLogPath->setText(fileDialog->selectedFiles().at(0));

        // Update ini file
        setConfigLog(fileDialog->selectedFiles().at(0));
    }

}

///
/// \brief CommConfig::on_btnCommConnect_released
///
void CommConfig::on_btnCommConnect_released()
{
    // Alread connected.
    if(mConnect){

        // Execute communication logic
        switch (ui->cboxCommSelect->currentIndex()) {

        case SERIAL:{
            qDebug() << "MavlinkHandler: Unsupport!";
        }break;

        case UDP:{
            qDebug() << "MavlinkHandler: UDP disconnect!";
        }break;

        case TCP:{
            qDebug() << "MavlinkHandler: TCP disconnect!";
        }break;

        case REPLAY:{
            qDebug() << "MavlinkHandler: Unsupport!";
        }break;

        default:
            break;
        }

        // Change button status
        mConnect = false;
        ui->btnCommConnect->setText("Connect");

        // Unregsister all message
        mMavlinkHandler->unregister_all(this);


    }else{

        // Execute communication logic
        switch (ui->cboxCommSelect->currentIndex()) {

        case SERIAL:
            qDebug() << "MavlinkHandler: Unsupport!";
            break;

        case UDP:{
            // Add udp connection
            setConfigNet(ui->leditCommNetIP->text(),ui->leditCommNetPort->text());
            if(ConnectionResult::Success  == mMavlinkHandler->add_udp_connection(mNetIp.toStdString(),mNetPort.toInt())){
                qDebug() << "MavlinkHandler: UDP connected!";
            }else{
                qDebug() << "MavlinkHandler: UDP connect failed";
                return;
            }

        }break;

        case TCP:{
            // Add tcp connection
            setConfigNet(ui->leditCommNetIP->text(),ui->leditCommNetPort->text());
            if(ConnectionResult::Success  == mMavlinkHandler->add_tcp_connection(mNetIp.toStdString(),mNetPort.toInt())){
                qDebug() << "MavlinkHandler: TCP connected!";
            }else{
                qDebug() << "MavlinkHandler: TCP connect failed";
                return;
            }
        }break;

        case REPLAY:
            qDebug() << "MavlinkHandler: Unsupport!";
            break;

        default:
            break;
        }

        // Regsister all message
        mMavlinkHandler->register_all([this](const mavlink_message_t& message) { cbMavMessage(message); },this);

        // Change button status
        mConnect = true;
        ui->btnCommConnect->setText("Disconnect");

        // set config comm select index
        setConfigCommSelect(ui->cboxCommSelect->currentIndex());
    }
}

///
/// \brief CommConfig::get_mavlink_msg_info
/// \param msg
/// \param msgInfo
/// \param dataType
/// \param i
/// \param j
/// \return
///
double CommConfig::get_mavlink_msg_info(const mavlink_message_t &msg,const mavlink_message_info_t *msgInfo, uint32_t dataType,uint32_t i,uint32_t j)
{
    double value = 0;
    /*
     * 按照数据在内存中模型进行解析
     * 若为数组(j != 0)，则为单个域的基地址(wire_offset) + 数组的下标(偏移)
     */
    if(MAVLINK_TYPE_CHAR == dataType){
         value = _MAV_RETURN_char(&msg,msgInfo->fields[i].wire_offset + 1 * j);
    }else if(MAVLINK_TYPE_UINT8_T == dataType){
        value = _MAV_RETURN_uint8_t(&msg,msgInfo->fields[i].wire_offset + 1 * j);
    }else if(MAVLINK_TYPE_INT8_T == dataType){
        value = _MAV_RETURN_int8_t(&msg,msgInfo->fields[i].wire_offset + 1 * j);
    }else if(MAVLINK_TYPE_UINT16_T == dataType){
        value = _MAV_RETURN_uint16_t(&msg,msgInfo->fields[i].wire_offset + 2 * j);
    }else if(MAVLINK_TYPE_INT16_T == dataType){
        value = _MAV_RETURN_int16_t(&msg,msgInfo->fields[i].wire_offset + 2 * j);
    }else if(MAVLINK_TYPE_UINT32_T == dataType){
        value = _MAV_RETURN_uint32_t(&msg,msgInfo->fields[i].wire_offset + 4 * j);
    }else if(MAVLINK_TYPE_INT32_T == dataType){
        value = _MAV_RETURN_int32_t(&msg,msgInfo->fields[i].wire_offset + 4 * j);
    }else if(MAVLINK_TYPE_UINT64_T == dataType){
        value = _MAV_RETURN_uint64_t(&msg,msgInfo->fields[i].wire_offset + 8 * j);
    }else if(MAVLINK_TYPE_DOUBLE == dataType){
        value = _MAV_RETURN_double(&msg,msgInfo->fields[i].wire_offset + 8 * j);
    }else if(MAVLINK_TYPE_FLOAT == dataType){
        value = _MAV_RETURN_float(&msg,msgInfo->fields[i].wire_offset + 4 * j);
    }else{
        ;
    }

    return value;
}

///
/// \brief CommConfig::cbMavMessage
/// \param msg
///
void CommConfig::cbMavMessage(const mavlink_message_t &msg)
{
    static qint64 timestamp_last = 0;
    static int recvmsgcnt = 0;

    // Get the fresh message infomation
    const mavlink_message_info_t *msgInfo = mavlink_get_message_info(&msg);
    QString nodeName(msgInfo->name);

    // New message object and insert field
    MESSAGE m;
    m.name = nodeName;
    m.sysid = msg.sysid;
    m.msgid = msg.msgid;
    m.nums = msgInfo->num_fields;

    for(uint32_t i = 0; i < msgInfo->num_fields; i++){
        FIELD f;

        QString(msgInfo->fields[i].name);

        f.name = msgInfo->fields[i].name;

        f.rtValue = get_mavlink_msg_info(msg,msgInfo,msgInfo->fields[i].type,i,0);

        f.sysid = msg.sysid;

        f.msgid = msg.msgid;

        m.fields.append(f);
    }

    recvmsgcnt++;

    qint64 timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();

    if(timestamp - timestamp_last > 1000){
        timestamp_last = timestamp;

        ui->linkButton->setText(QString("Message: %1 pcs/s").arg(recvmsgcnt));

        recvmsgcnt = 0;
    }

    // Emit the signal
    emit newSigMavMsg(m);
}
